﻿using System;
using System.ComponentModel;
using System.Text;

namespace Taschenrechner {
    class ProcessManager {

        const int bufferLength = 30;
        readonly OperatorMapper operatorMapper = new OperatorMapper();
        readonly ArithmeticDispatcher arithmetic = new ArithmeticDispatcher();

        StringBuilder buffer = new StringBuilder(bufferLength);
        bool isSeparator;
        bool isNumber1;
        Number number1;
        ArithmeticOperator opcode;
        Term term;
        double result = 0;

        internal string NumberString {
            get { return buffer.ToString(); }
        }

        internal string ResultString {
            get { return result.ToString(); }
        }

        internal ProcessManager() {
            Init();
        }

        private void Init() {
            RemoveBuffer();
            isNumber1 = true;
            isSeparator = false;
        }

        internal void ClearEntry() {
            RemoveBuffer();
        }

        internal void Clear() {
            Init();
        }

        internal bool ProcessDigitChar(char charToProcess) {
            if (buffer.Length < bufferLength) {
                if ((charToProcess != ValidChar.separator) || 
                    (charToProcess == ValidChar.separator && !isSeparator)) {
                    AppendBuffer(charToProcess);
                        if (charToProcess == ValidChar.separator) {
                        isSeparator = true;
                    }
                    return true;
                }
            }
            return false;
        }

        internal bool ProcessOperatorChar(char charToProcess) {
            if (buffer.Length > 0) {
                if (isNumber1) {
                    SetStatusToContinue(NumberString, charToProcess);
                    isNumber1 = false;
                }
                else {
                    ComputeResult();
                    SetStatusToContinue(result.ToString(), charToProcess);
                }
                return true;
            }
            return false;
        }

        private void SetStatusToContinue(string numberString, char charToProcess) {
            // es kann keine Exception ausgelöst werden, da der Puffer zu klein ist => keine Fehlerbehandlung
            number1 = new Number(numberString);
            opcode = operatorMapper.Operation[charToProcess];
            isSeparator = false;
            RemoveBuffer();
            return;
        }

        internal bool ProcessResultChar() {
            if (buffer.Length > 0 && !isNumber1) {
                ComputeResult();
                Init();
                return true;
            }
            return false;
        }

        private void ComputeResult() {
            Number number2 = new Number(buffer.ToString());
            term = new Term(number1, opcode, number2);
            try {
                ResultOperation();
            }
            catch (OverflowException e) {
                Init();
                throw e;
            }
            return;
        }

        internal event PropertyChangedEventHandler ProcessManagerPropertyChanged;

        private void NotifyPropertyChanged(String info) {
            if (ProcessManagerPropertyChanged != null) {
                ProcessManagerPropertyChanged(this, new PropertyChangedEventArgs(info));
            }
        }

        private void RemoveBuffer() {
            buffer.Remove(0, buffer.Length);
            NotifyPropertyChanged("NumberString");
        }

        private void AppendBuffer(char charToProcess) {
            buffer.Append(charToProcess);
            NotifyPropertyChanged("NumberString");
        }

        private void ResultOperation() {
            result = arithmetic.Operation[term.ArithmeticOperator].Compute(term);
            NotifyPropertyChanged("ResultString");
        }

    }
}
